import { BadRequestException, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Client } from 'minio';
import { createHash } from 'crypto';
import { extname } from 'path';

import { BufferedFile } from 'src/types';
import { Storage } from './entities/storage.entity';

@Injectable()
export class StorageService {
  private readonly client: Client;

  constructor(
    @InjectRepository(Storage)
    private readonly storeRepository: Repository<Storage>,
    private readonly configService: ConfigService,
  ) {
    this.client = new Client({
      endPoint: this.configService.get('MINIO_HOST') ?? 'localhost',
      port: this.configService.get<number>('MINIO_PORT') ?? 9000,
      useSSL: false,
      accessKey: this.configService.get('MINIO_ACCESS_KEY'),
      secretKey: this.configService.get('MINIO_SECRET_KEY'),
    });
  }

  get bucketName() {
    return this.configService.get<string>('MINIO_BUCKET_NAME') ?? 'files';
  }

  async uploadFile(file: BufferedFile) {
    const id = createHash('md5').update(file.buffer).digest('hex');
    const ext = extname(file.originalname);

    await this.client.putObject(this.bucketName, `${id}${ext}`, file.buffer);

    let store = await this.storeRepository.findOne({ where: { id } });

    if (store) {
      throw new BadRequestException('文件已存在');
    }

    store = new Storage();
    store.id = id;
    store.name = Buffer.from(file.originalname, 'binary').toString();
    store.type = ext;

    return this.storeRepository.save(store);
  }

  async uploadFiles(files: BufferedFile[]) {
    return Promise.all(files.map((file) => this.uploadFile(file)));
  }

  async deleteFile(id: string) {
    const file = await this.findOne(id);

    if (!file) {
      throw new BadRequestException('文件不存在');
    }

    await this.client.removeObject(this.bucketName, `${file.id}${file.type}`);

    return this.storeRepository.remove(file);
  }

  async downloadFile(id: string) {
    const file = await this.findOne(id);
    const stream = await this.client.getObject(this.bucketName, `${file.id}${file.type}`);
    return { file, stream };
  }

  async findAll(current: number = 1, pageSize: number = 10) {
    const [data, total] = await this.storeRepository.findAndCount({
      skip: (current - 1) * pageSize,
      take: pageSize,
      order: { update_time: 'DESC' }, // 可选，按id倒序
    });

    return {
      data,
      total,
      current,
      pageSize,
      pages: Math.ceil(total / pageSize),
    };
  }

  async findOne(id: string) {
    const file = await this.storeRepository.findOne({ where: { id } });

    if (!file) {
      throw new BadRequestException('文件不存在');
    }

    return file;
  }
}
